﻿using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Surging.Core.CPlatform;
using Surging.Core.CPlatform.Engines;
using Surging.Core.CPlatform.Module;
using Surging.Core.CPlatform.Runtime.Server;
using Surging.Core.CPlatform.Serialization;
using Surging.Core.KestrelHttpServer.Extensions;
using Surging.Core.KestrelHttpServer.Internal; 
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Surging.Core.CPlatform.Routing;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Surging.Core.KestrelHttpServer.Filters;
using Surging.Core.CPlatform.Messages;
using System.Diagnostics;
using Surging.Core.CPlatform.Configurations;
using Surging.Core.CPlatform.Diagnostics;
using Surging.Core.CPlatform.Utilities;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Localization;

namespace Surging.Core.KestrelHttpServer
{
    public class KestrelHttpMessageListener : HttpMessageListener, IDisposable
    {
        private readonly ILogger<KestrelHttpMessageListener> _logger;
#if NETCOREAPP
        private IHost _host;
        //private readonly IHostApplicationLifetime _lifetime;
#else
        private IWebHost _host;
        //private readonly IServiceEngineLifetime _lifetime;
#endif
        private bool _isCompleted;
        private readonly ISerializer<string> _serializer;
        private readonly IServiceEngineLifetime _lifetime;
        private readonly IModuleProvider _moduleProvider;
        private readonly CPlatformContainer _container;
        private readonly IServiceRouteProvider _serviceRouteProvider;
        private readonly DiagnosticListener _diagnosticListener;

        public KestrelHttpMessageListener(ILogger<KestrelHttpMessageListener> logger,
           ISerializer<string> serializer,
//#if NETCOREAPP
//       IHostApplicationLifetime lifetime,
//#else
        IServiceEngineLifetime lifetime,
//#endif
       IModuleProvider moduleProvider,
           IServiceRouteProvider serviceRouteProvider,
            CPlatformContainer container) : base(logger, serializer, serviceRouteProvider)
        {
            _logger = logger;
            _serializer = serializer;
            _lifetime = lifetime;
            _moduleProvider = moduleProvider;
            _container = container;
            _serviceRouteProvider = serviceRouteProvider;
            _diagnosticListener = new DiagnosticListener(DiagnosticListenerExtensions.DiagnosticListenerName);
        }

        public async Task StartAsync(IPAddress address,int? port)
        { 
            try
            {
                if (AppConfig.ServerOptions.DockerDeployMode == DockerDeployMode.Swarm)
                {
                    address = IPAddress.Any;
                }
                _logger.LogInformation($"Kestrel StartAsync...");
#if NETCOREAPP
                var hostBuilder = Host.CreateDefaultBuilder()
                    .ConfigureLogging(configure => configure.ClearProviders())
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseKestrel((context, options) =>
                        {
                            options.Limits.MinRequestBodyDataRate = null;
                            options.Limits.MinResponseDataRate = null;
                            options.Limits.MaxRequestBodySize = null;
                            options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30);
                            if (port != null && port > 0)
                            {
                                options.Listen(address, port.Value, listenOptions =>
                                {
                                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                                });
                            }

                            ConfigureHost(context, options, address);
                        })
                            .ConfigureServices(ConfigureServices)
                            .Configure(AppResolve);

                        if (Directory.Exists(AppConfig.ServerOptions.WebRootPath))
                        {
                            webBuilder.UseWebRoot(AppConfig.ServerOptions.WebRootPath);
                        }
                    });

                _host = hostBuilder.Build();
                _lifetime.ServiceEngineStarted.Register(async () =>
                {
                    if (_moduleProvider.Modules.Any(p => p.ModuleName == "SwaggerModule" && p.Enable))
                    {
                        var httpProtocol = GetHttpProtocol();
                        _logger.LogInformation($"Kestrel主机将启动,Swagger文档地址为:{httpProtocol}://{address}:{port}/swagger/index.html");
                    }
                    else
                    {
                        _logger.LogInformation($"Kestrel主机即将启动");
                    }
                    await _host.RunAsync();
                });
#else
                var hostBuilder = new WebHostBuilder()
                  .UseContentRoot(Directory.GetCurrentDirectory())
                  .UseKestrel((context,options) =>
                  {
                      options.Limits.MinRequestBodyDataRate = null;
                      options.Limits.MinResponseDataRate = null;
                      options.Limits.MaxRequestBodySize = null;
                      options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30);
                      if (port != null && port > 0)
                          options.Listen(address, port.Value, listenOptions =>
                          {
                              listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                          });
                      ConfigureHost(context, options, address);

                  })
                  .ConfigureServices(ConfigureServices)
                  .ConfigureLogging((logger) =>
                  {
                      logger.AddConfiguration(
                             CPlatform.AppConfig.GetSection("Logging"));
                  })
                  .Configure(AppResolve);

                if (Directory.Exists(CPlatform.AppConfig.ServerOptions.WebRootPath))
                    hostBuilder = hostBuilder.UseWebRoot(CPlatform.AppConfig.ServerOptions.WebRootPath);
                _host = hostBuilder.Build();
                _lifetime.ServiceEngineStarted.Register(async () =>
                {
                    await _host.RunAsync();
                });
#endif
            }
            catch (Exception ex)
            {
                _logger.LogError($"http服务主机启动失败，监听地址：{address}:{port}。 \n{ex.ToString()}");
                //_logger.LogError($"http服务主机启动失败，监听地址：{address}:{port}。 ");
            }

        }
        private string GetHttpProtocol()
        {
            var httpProtocol = "http";
            if (_moduleProvider.Modules.Any(p => p.ModuleName == "StageModule"))
            {
                var stageModule = _moduleProvider.Modules.First(p => p.ModuleName == "StageModule" && p.Enable);
                var enableHttpsObj = stageModule.GetType().GetProperty("EnableHttps")?.GetValue(stageModule);
                if (enableHttpsObj != null)
                {
                    httpProtocol = Convert.ToBoolean(enableHttpsObj) ? "https" : "http";
                }
            }

            return httpProtocol;
        }

        public void ConfigureHost(WebHostBuilderContext context, KestrelServerOptions options,IPAddress ipAddress)
        {
            _moduleProvider.ConfigureHost(new WebHostContext(context, options, ipAddress));
        }

        public void ConfigureServices(IServiceCollection services)
        { 
            var builder = new ContainerBuilder();
            ConfigureLocalizationServices(services);
#if NETCOREAPP
            services.AddControllers();
#else
            services.AddMvc();
#endif
            services.AddCors(options =>
            {
                options.AddPolicy("any", policyBuilder =>
                {
                    policyBuilder.WithOrigins("*");
                    policyBuilder.AllowAnyHeader();
                    policyBuilder.AllowAnyMethod();
                    policyBuilder.AllowAnyOrigin();
                    //policyBuilder.AllowCredentials();
                });
            });

            _moduleProvider.ConfigureServices(new ConfigurationContext(services,
                _moduleProvider.Modules,
                _moduleProvider.VirtualPaths,
                AppConfig.Configuration));
            builder.Populate(services); 
            //builder.Update(_container.Current.ComponentRegistry);
        }

        private void AppResolve(IApplicationBuilder app)
        {
#if NETCOREAPP
            app.UseDeveloperExceptionPage();
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
            });
#else
            app.UseStaticFiles();
            app.UseMvc();
#endif
            app.UseCors("any");
            _moduleProvider.Initialize(new ApplicationInitializationContext(app, _moduleProvider.Modules,
                _moduleProvider.VirtualPaths,
                AppConfig.Configuration));
            app.Run(async (context) =>
            {
                var messageId = Guid.NewGuid().ToString("N");
                var sender = new HttpServerMessageSender(_serializer, context,_diagnosticListener);
                try
                {
                    var filters = app.ApplicationServices.GetServices<IAuthorizationFilter>();
                    var isSuccess = await OnAuthorization(context, sender, messageId, filters);
                    if (isSuccess)
                    {
                        var actionFilters = app.ApplicationServices.GetServices<IActionFilter>();
                        await OnReceived(sender, messageId, context, actionFilters);
                    }
                }
                catch (Exception ex)
                {
                    var filters = app.ApplicationServices.GetServices<IExceptionFilter>();
                    WirteDiagnosticError(messageId, ex);
                    await OnException(context, sender, messageId, ex, filters);
                }
            });
        }
        private static void ConfigureLocalizationServices(IServiceCollection services)
        {
            var cultures = new System.Collections.Generic.List<System.Globalization.CultureInfo> { new System.Globalization.CultureInfo("zh-Hans"), new System.Globalization.CultureInfo("en") };
            services.Configure<RequestLocalizationOptions>(options =>
            {
                options.DefaultRequestCulture = new RequestCulture("zh-Hans");
                options.SupportedCultures = cultures;
                options.SupportedUICultures = cultures;
            });
        }
        private void WirteDiagnosticError(string messageId,Exception ex)
        {
            _diagnosticListener.WriteTransportError(CPlatform.Diagnostics.TransportType.Rest, new TransportErrorEventData(new DiagnosticMessage
            {
                Id = messageId
            }, ex));
        }

        public void Dispose()
        {
            _host.Dispose();
        }

    }
}
